'''
Attr. Defines the Attribute classes for use with XCP.
Copyright (C) 2006  Jonathan Claggett

This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option) any
later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
details.

You should have received a copy of the GNU Lesser General Public License along
with this library; if not, write to the Free Software Foundation, Inc., 51
Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

You may contact the author at <jonathan@claggett.org>
'''
if __debug__: import pdb

import type

# Attr Exceptions
class AttrError(Exception):
    pass

def apply_defs(attrs, attr_defs):
    '''
    The apply_defs is used to process a dictionary of attributes by setting
    default values and by converting the original string values to the proper
    types.
    
    AttrError exceptions are raised if required attributes are missing or
    unknown attributes are present.
    '''
    # Loop through the attribute definitions and apply them.
    for name, attr_def in attr_defs.items():
        attrs[name] = attr_def.apply(attrs.get(name))

    # Confirm that the number of attributes is not greater than the defs.
    if len(attrs) > len(attr_defs):
        for name in attrs.keys():
            if not attr_defs.has_key(name):
                raise AttrError, 'Unknown attribute: %s' % (name,)

    return attrs

class Required(object):
    '''
    The Required class is a trivial class used to inform the Attr class that
    there should never be a default value for that attribute.
    '''
    pass

class Attr(object):
    '''
    The Attr class specifies the valid types an Element attribute may have as
    well as its default value (if any).
    '''
    def __init__(self, type=type.ANY, default=Required):
        self.type = type
        self.default = default

    def apply(self, value=None):
        if value is None:
            if self.default is Required:
                raise AttrError, "Missing required attr: %s." % (name,)
            value = self.default
        else:
            value = type.cast(value, self.type)
        return value

# Unit Testing
if __debug__:
    import unittest

    class TestAttrs(unittest.TestCase):

        def test_trivial(self):
            attr_defs = dict(
                newline = Attr(type.BOOL, default = False),
                format = Attr(type.BOOL),
                quotes = Attr(type.INT | type.HEX, default = None) )
            attr_defs['if'] = Attr(type.INT, default = 13)

            attrs = dict(newline='TRUE', format='false')
            apply_defs(attrs, attr_defs)
            self.assertEqual(attrs['newline'], True)
            self.assertEqual(attrs['format'], False)
            self.assertEqual(attrs['quotes'], None)
            self.assertEqual(attrs['if'], 13)

    if __name__ == '__main__':
        unittest.main()
        #test_support.run_unittest( cast.TestCast, TestParser )

# vim: ai et sw=4 ts=4
